/*******************************************************************************
 * Copyright (c) 2011 Olexandr V. Shaposhnikov.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Olexandr V. Shaposhnikov - initial API and implementation and/or initial documentation
 *******************************************************************************/
package com.funkyroach.use.core.completion;

import java.util.HashSet;
import java.util.Set;

import org.eclipse.core.runtime.Assert;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.parser.ISourceParser;
import org.eclipse.dltk.ast.statements.Block;
import org.eclipse.dltk.codeassist.ScriptCompletionEngine;
import org.eclipse.dltk.compiler.env.IModuleSource;
import org.eclipse.dltk.compiler.util.Util;
import org.eclipse.dltk.core.CompletionProposal;
import org.eclipse.dltk.core.DLTKLanguageManager;
import org.eclipse.dltk.core.IField;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.mixin.IMixinElement;
import org.eclipse.dltk.internal.core.SourceType;
import org.eclipse.dltk.ti.DLTKTypeInferenceEngine;

import com.funkyroach.use.core.ast.USClassBody;
import com.funkyroach.use.core.ast.USClassDeclaration;
import com.funkyroach.use.core.ast.USModuleDeclaration;
import com.funkyroach.use.core.language.USKeywords;
import com.funkyroach.use.core.language.USNature;
import com.funkyroach.use.core.mixin.USMixinModel;
import com.funkyroach.use.core.selection.USSelectionEngine;

public class USCompletionEngine extends ScriptCompletionEngine {

	private final static int RELEVANCE_KEYWORD = 1000000;
	private final static int RELEVANCE_TYPE = 2000000;
	private final static int RELEVANCE_METHODS = 10000000;
	private final static int RELEVANCE_VARIABLES = 100000000;

	private ISourceModule fCurrentModule;
	private HashSet<String> alreadyCompletedNames = new HashSet<String>();
	private ASTNode fMinimalNode;
	private ISourceParser parser;
	private String fContent;
	private String fWordStarting;
	private ASTNode[] fWayToNode;
	private USModuleDeclaration fModule;
	private USMixinModel mixinModel = null;

	public USCompletionEngine() {
		this.parser = DLTKLanguageManager.getSourceParser(USNature.NATURE_ID);
	}

	@Override
	public void complete(IModuleSource module, int position, int i) {
		Assert.isNotNull(module);
		fCurrentModule = (ISourceModule) module;
		mixinModel = USMixinModel.instance(fCurrentModule.getScriptProject());
		//mixinModel.clear();
		
		requestor.beginReporting();

		alreadyCompletedNames.clear();
		fModule = (USModuleDeclaration) parser.parse(module, null);
		System.out.println("MODULE:" + fModule);

		fMinimalNode = USSelectionEngine.findMinimalNode(fModule, position,
				position);
		fWayToNode = USSelectionEngine.collectWayToNode(fModule, fMinimalNode);

		fContent = module.getSourceContents();
		fWordStarting = getWordStarting(fContent, position, 10);
		this.setSourceRange(position - fWordStarting.length(), position);
		try {
			completeKeywords();
			// TODO
			if (fMinimalNode != null) {
				ISourceModule mixinSourceModule = mixinModel.findModule(fModule.getName());
				System.out.println("mixinSourceModule = " + mixinSourceModule);
				System.out.println("minimalNode = " + fMinimalNode.getClass());
				if (fMinimalNode instanceof Block) {
					if (parentNode(1) instanceof USClassBody) {
						USClassDeclaration astClass = (USClassDeclaration)parentNode(1);
						SourceType mixinElement = mixinModel.findClass(astClass.getName());
						
						System.out.println("parentNode = "
								+ mixinElement);
					}
				}
			}

			// >>>>>>>>>>>> TEST ONLY
			// reportField(new USStubField(fCurrentModule, "XXXX", 0, 0),
			// RELEVANCE_VARIABLES);
			// <<<<<<<<<<<<

		} finally {
			requestor.beginReporting();
		}
	}

	private void completeKeywords() {
		// TODO add context filter
		Set<String> keywords = USKeywords.instance().getStartsWith(
				fWordStarting);
		for (String keyword : keywords) {
			reportKeyWord(keyword);
		}
	}

	private ASTNode parentNode(int index) {

		if (fWayToNode.length - index >= 0) {
			return fWayToNode[fWayToNode.length - index];
		}
		return fWayToNode[0];
	}

	private void reportKeyWord(String name) {
		noProposal = false;
		if (!requestor.isIgnored(CompletionProposal.KEYWORD)) {
			CompletionProposal proposal = createProposal(
					CompletionProposal.KEYWORD, actualCompletionPosition);

			proposal.setName(name);
			proposal.setCompletion(name);

			proposal.setReplaceRange(this.startPosition - this.offset,
					this.endPosition - this.offset);
			proposal.setRelevance(RELEVANCE_KEYWORD);

			this.requestor.accept(proposal);
			// XXX
			this.printDebug(proposal);
		}
	}

	private void reportField(IField field, int rel) {
		String elementName = field.getElementName();

		if (alreadyCompletedNames.contains(elementName)) {
			return;
		}
		alreadyCompletedNames.add(elementName);
		noProposal = false;

		if (!requestor.isIgnored(CompletionProposal.FIELD_REF)) {
			CompletionProposal proposal = createProposal(
					CompletionProposal.FIELD_REF, actualCompletionPosition);

			proposal.setModelElement(field);
			proposal.setName(elementName);
			proposal.setCompletion(elementName);
			proposal.setReplaceRange(startPosition - offset, endPosition
					- offset);
			proposal.setRelevance(rel);
			// XXX
			//this.printDebug(proposal);
			requestor.accept(proposal);
		}

	}

	private String getWordStarting(String content, int position, int maxLen) {
		if (position <= 0 || position > content.length())
			return Util.EMPTY_STRING;
		final int original = position;
		while (position > 0
				&& maxLen > 0
				&& (Character
						.isJavaIdentifierPart(content.charAt(position - 1)))) {
			--position;
			--maxLen;
		}
		return content.substring(position, original);
	}
}
